//This file includes two classes. Dator and Setting<T>
//
//Dator is an abstract class used by the SettingsManager
//It provides two key methods Set() and ToString(), and a number of useful operators
//
//Setting<T> is a generic derivation of Dator.
//Setting<T> will work for all the basic types (i.e. int, char, bool)
//However due to an eccentriticy of the way std::stream define operator>> for
//  strings and int8, this trick will not work for string types. 
//  For strings use the StringSetting class instead. 
//  There is no implementation for int8 at present
//  Using using a Setting<string> or Setting<int8> will result in loss of data.
//
//Note: Any class that overrides operator<< and operator>> can also use Setting<T>
//Otherwise a custom class derived from Dator must be written to provide the proper
//Set and ToString() functions

#ifndef ENGINE_DATOR_H
#define ENGINE_DATOR_H

#include <stdlib.h>
#include <sstream>
#include <exception>

#include "base/types.h"
#include "base/string_wrapper.h"
#include "base/result_codes.h"

namespace engine
{

//This Class allows the SettingsManager to not know the type of the settings it stores
//The generic type used for all settings is an std::string
//Therefore a Dator object must proved overrides for the functions Set() and ToString()
class Dator {
public:
    //virtual destructor so that derived classes are properly deleted
    virtual ~Dator(){};

    //This function must be overriden to properly obtain the string representation
    //of the variable to which this dator is bound
    virtual int ToString(std::string  & value) const = 0;
    
    //This function must be overriden to properly set the value of the variable
    //to which this dator is bound
    virtual int Set(const std::string & value) = 0;


    operator std::string() {
        std::string value;
        ToString(value);
        return value; 
    }

    Dator & operator =(std::string &value) {
        Set(value);
        return *this;
    }

    bool operator == (std::string &value) {
        std::string this_value;
        ToString(this_value);
        return (value == this_value); 
    }
    
    bool operator !=(std::string &value) { 
        std::string this_value;
        ToString(this_value);
        return (value != this_value); 
    }

protected:
    Dator(){}
    Dator(Dator &copy){(*this)= copy;}  

};

//This is a generic derivation of the Dator class
//Taking advantage of the fact that stdlib defines operator<< and operator>> 
//for all the basic types (int, float, char, etc), Setting<T> uses stringstream
//to generate generic Set() and ToString() functions
//
//Instead of deriving Dator. This generic derivation can be used by simply
//providing operator<< and operator>> for any new setting type
template<class T>
class Setting : public Dator {
public:

    //Note that this constructor binds this Setting to a variable reference
    Setting(T & target) : target_(target) {}

    //Use stringstream to generate typed values from strings
    int Set(const std::string & value)
    {
        try {
            std::stringstream str;
            str.setf(std::ios::boolalpha);
            str.unsetf(std::ios::skipws);
            str.setf(std::ios::basefield, std::ios::dec);
            str.setf(std::ios::floatfield, std::ios::fixed);
            str << value;        
            str >> (T)target_;
        } catch (std::exception e){
            return base::ER_TEMPLATING;
        }

        return base::OK_OK;
    }
  
    //Use stringstream to generate strings from typed values
    int ToString(std::string & value) const
    {        
        try{
            std::stringstream str;
            str.setf(std::ios::boolalpha);
            str.unsetf(std::ios::skipws);
            str.setf(std::ios::basefield, std::ios::dec);
            str.setf(std::ios::floatfield, std::ios::fixed);
            str << target_;
            str >> value;
        } catch (std::exception e){
            return base::ER_TEMPLATING;
        }
        
        return base::OK_OK;
    }

    //Typed value accessor
    T & Get() const { return target_; }


private:
    T& target_;
    
    DISALLOW_IMPLICIT_CONSTRUCTORS(Setting);
};

#define CharSetting Setting<char>
#define BoolSetting Setting<bool>  
#define ShortSetting Setting<short>
#define IntSetting Setting<int>    
#define FloatSetting Setting<float>  
#define DoubleSetting Setting<double>

class StringSetting : public Dator {
public:
    //Note that this constructor binds this Setting to a variable reference
    StringSetting(std::string & target) : target_(target) {}

    //Use stringstream to generate typed values from strings
    int Set(const std::string & value)
    {
        target_ = value;
        return base::OK_OK;
    }
  
    //Use stringstream to generate strings from typed values
    int ToString(std::string & value) const
    {        
        value = target_;        
        return base::OK_OK;
    }

    //Typed value accessor
    std::string & Get() const { return target_; }


private:
    std::string& target_;
    
    DISALLOW_IMPLICIT_CONSTRUCTORS(StringSetting);
};

} //namespace
#endif //ENGINE_DATOR_H